/**
 * machine父类
 * @author 冯永泽
 * @date 2019/12/4
 */
const dao = require('./dao');
const daoClient = new dao();
const config = require('../config/index.js');
const querystring = require("querystring");
const date = require('silly-datetime');
const kafka = require('../utils/kafka-utils');
class machine {
    constructor(http, tcpServe, topics) {

        this.PLCip = config.PLCip();
        this.scanIp = config.scanIp();
        // tcp实类
        this.tcpServe = tcpServe;
        // http实类
        this.http = http;
        // 配置的kafkaTopics
        this.topics = topics;
        // 连接的客户端对象
        this.clients = {};
        this.tcpServe.tcpError();
        this.tcpServe.tcpClose();
        // 发送报文的PLC
        this.machineType = '';
        // 扫码枪粘包后的数据
        this.scanResult = '';
        this.scheduler();
        // kafka对象数组
        this.kafkaOrder = [];
        // 根据topic判断是否在插入数据库
        this.insertWorking = false;
        this.arr = [];
    }

    /**
     * 服务器连接时启动轮询调度
     * @param   
     */
    scheduler() {}
    // tcp有设备连接
    tcpConnect(cb) {
        this.tcpServe.tcpConnection((socket) => {
            socket.setEncoding('hex');
            cb(socket);
        });
    }
    /**
     * 当有客户端连接上时
     * @param {Object} socket 
     */
    clientConnected(socket) {

        this.tcpData(socket);
        this.tcpServe.socketClose(socket, (data) => {
            let key = socket.remoteAddress;
            delete this.clients[key];
        });
        this.tcpServe.socketError(socket);
    }
    /**
     * tcp接受消息，判断头信息是什么，再决定运行什么程序，如子类判断不足可在socketFunction自行添加
     * @param {Object} socket 
     * @param {Function} socketFunction 
     */
    tcpData(socket) {
        this.tcpServe.socketData(socket, (data) => {
            let key = socket.remoteAddress;
            let returnData = {
                isSend: false,
                content: ''
            }
            let isScan = false;
            for (let k in this.scanIp) {
                if (this.scanIp[k] === key) {
                    isScan = true;
                }
            }
            if (isScan) {
                this.scanStickData(data, key);
            } else {
                let arr = this.splitData(data);
                // 把数组的16进制转换为10进制
                arr = arr.map(item => {
                    return this.convertDecimal(this.stringReverse(item));
                });
                let type = arr[1];
                let res = this.receive(key, arr, type);
                if(!res) return
                let content = this.messageResponse(arr[0], arr[1], arr[2]) + res.content;
                if (res.isSend) {
                    // 延时0.1秒应答
                    setTimeout(() => {
                        socket.write(content, 'hex');
                    }, 100);
                }
            }
        })
    }
    /**
     * TCP超时
     * @param {*} socket 
     * @param {Number} time  
     * @param {Function} cb  回调方法
     */
    tcpTimeout(socket, time, cb) {
        this.tcpServe.socketTimeout(socket, time, cb);
    }
    receive(arr, type, randomNumber) {}
    /**
     * tcp向特定id发送信息
     * @param {Number} id 
     * @param {string} 16进制字符串  
     */
    tcpSend(key, content) {
        //获取指定client
        this.clients[key].socket.write(content, 'hex');
    }
    /**
     * plc发送连接成功后信息
     * @param {Object} socket client连接后对象
     * @param {Array} arr PLC上报的报文
     * @param {Number} type 消息类型
     * @param {Number} randomNumber 随机数
     * @param {Function} selfFunction 私自方法
     * @memberof machine
     */
    receiveConnect(socket, arr, type, randomNumber, selfFunction = () => {}) {
        this.Sockets.push({
            id: arr[0],
            socket: socket
        });
        selfFunction();
    }
    /**
     * PCL状态上报
     * @param {Object} data  接收的信息
     * @param {Function} statusFunction 状态正常时的回调函数
     * @param {Function} errorFunction 状态异常时的回调函数
     */
    receiveStatus(data, statusFunction = () => {}, errorFunction = () => {}) {
        this.status = data.status;
        if (this.deviceStatus === '异常') {
            errorFunction(data);
        } else {
            statusFunction(data);
        }
    }

    /**
     * PCL状态信息上报
     * @param {Object} data  接收的信息
     */
    receivePLCStatus(data, cb = () => {}) {
        cb(data);
    }
    /**
     * PLC发送制餐完成
     * @param {Function} cb 回调函数
     */
    receiveCompletion(data, cb = () => {}) {
        cb(data);
    }
    /**
     * 扫描二维码PLC发送信息
     * @param {Function} cb 回调函数
     */
    receiveScan(cb = () => {}) {
        if (data.mealId === this.mealId) {
            cb();
        }
    }
    /**
     * PLC发送超时时间
     * @param {Function} cb 回调方法
     */
    receiveOvertime(data, cb = () => {}) {
        cb(data);
    }
    /**
     * 向PLC下发制单
     * @param {Function} cb 回调函数
     */
    sendMakeMeal(cb = () => {}) {
        // 当状态为正常时才能下发制单
        cb();
    }
    /**
     * 发送PLC打开出餐口
     * @param {Function} cb 回调方法
     */
    sendOutlet(cb = () => {}) {
        cb();
    }
    /**
     * 异常状态上报云端
     * @param {Function} cb 回调函数
     */
    postAbnormal(param, cb = () => {}) {
        let params = {
            sysAppName: 'business',
            service: 'MessageService',
            method: 'deviceFailure',
            storeId: param.storeId,
            code: param.code,
            message: param.message
        }
        this.http.post(params, (err, res, body) => {
            cb(JSON.parse(body));
        });
    }
    /**
     *
     *  上发菜品信息
     * @param {obj} param 菜品信息
     * @param {Function} cb
     * @memberof machine
     */
    postMealAbnormal(param = {
        storeId: '',
        orderId: '',
        productId: '',
        productStatus: '',
        deviceLevelId: '',
        orderDetailId: ''
    }, cb = () => {}) {
        console.log(param);
        let params = {
            sysAppName: 'business',
            service: 'AutoSellerOrderService',
            method: 'updateProductStatusByDetailID',
            storeId: param.storeId, // 门店ID
            orderId: param.orderId, // 订单ID
            productId: param.productId, // 菜品ID
            productStatus: param.productStatus, // 菜品状态   22：处理中/  58：待取餐/   510：已取餐/  999：失败  //520 超时处理（去人工取餐）
            deviceLevelId: param.deviceLevelId, // 设备层ID
            orderDetailId: param.orderDetailId // 订单明细ID
        }
        this.http.post(params, (err, res, body) => {
            cb(JSON.parse(body));
        });
    }
    /**
     *
     * 根据订单ID获取菜品明细
     *
     * @param {String} orderId 订单号
     * @param {Function} cb 回调函数
     * @memberof machine
     */
    /** */
    getOrderDetail(param = {
        orderId: '',
        deviceCode: '',
        listenKafka: ''
    }, cb = () => {}) {
        let params = {
            sysAppName: 'business',
            service: 'OrderService',
            method: 'scanFindByOrder',
            orderId: param.orderId,
            deviceCode: param.deviceCode,
            deviceId: param.deviceId
        };
        this.http.post(params, (err, res, body) => {
            cb(JSON.parse(body));
        });
    }

    login() {
        this.http.login((err, res, body) => {
            console.log(body);
        })
    }
    /**
     *
     * 字符串颠倒
     * @memberof machine
     */
    stringReverse(str) {
        let reg = /.{2}/g;
        let arr = str.match(reg);
        return arr[1] + arr[0];
    }
    /**
     * 把接受的报文按每4个字符拆分
     * @returns 返回报文数组
     */
    splitData(data) {
        let arr = [];
        let reg = /.{4}/g;
        arr = data.match(reg);
        return arr;
    }
    /**
     * 十进制转换成十六进制
     * @param {Number} num
     * @returns 返回十六进制报文
     */
    convertHex(num) {
        num = num.toString(16);
        let str = '';
        if (num.length < 4) {
            num = (Array(4).join(0) + num).slice(-4);
        }
        str = num;
        return str;
    }
    /**
     * 十六进制转换成十进制
     * @param {*} str
     * @return 返回10进制报文
     */
    convertDecimal(str) {
        return Number('0X' + str);
    }
    /**
     * 十进制转换成2进制
     *
     * @param {*} str
     * @memberof machine
     */
    convertBinary(str) {
        return str.toString(2);
    }
    /**
     *
     * 传入需要发送给PLC的各个10进制字符串，返回一串16进制报文
     * @param {*} values 10进制字符串
     * @return {String} str 返回16进制报文
     */
    messageResponse(...values) {
        let str = '';
        values.forEach(item => {
            str += this.stringReverse(this.convertHex(item));
        });
        return str;
    }
    /**
     *
     * 获取四位随机数
     * @returns
     * @memberof machine
     */
    getRandom() {
        return parseInt(Math.random() * 9000 + 1000)
    }
    /**
     *
     * 扫码枪粘包操作
     * @param {*} data 粘包前数据
     * @memberof machine
     */
    scanStickData(data, key) {
        this.scanResult += data;
        // 判断是否字符串是否有0D结束符
        if (this.scanResult.match(RegExp(/0d/))) {
            this.scanResult = Buffer.from(this.scanResult, 'hex').toString();
            // 处理订单二维码网址
            this.receiveScanData(this.scanResult, key);
            this.scanResult = '';
        }
    }
    /**
     * 转行列序号
     * @param {Number} row 行号 
     * @param {Number} cloum 列号
     * @param {Number} rows   设置的行号
     */
    toList(row,cloum,rows) {
        return rows*(row-1)+cloum;
    }
    /**
     * 切换规格ID
     * @param {String} params 规格参数 
     * @return {Array}  规格对应字段的ID
     */
    toSpecinfo(params) {
        console.log("params",params);
        return params.match(/[0-9]+/ig,"");
    }
    /**
     * 补0操作
     *
     * @param {Number} num 传入的数字 
     * @param {Number} n 字符长度
     * @returns {String}
     * @memberof machine
     */
    prefixInteger(num, n) {
        return (Array(n).join(0) + num).slice(-n);
    }
    /**
     * 扫码枪获取信息后方法
     *
     * @param {*} data 粘包后数据
     * @memberof machine
     */
    receiveScanData(data) {}
    listenKafka() {
        let kafkaClient = new kafka(config.kafkaUrl(), this.topics);
        kafkaClient.message((message) => {
            console.log(message);
            this.kafkaOrder.push(JSON.parse(message.value));
            this.splitKafka(message.topic, this.kafkaOrder.length - 1);
        });
    }
    /**
     * 递归处理kafka订单
     *
     * @param {*} topic kafka头信息
     * @memberof machine
     */
    splitKafka(topic) {
        this.insertWorking = false;
        // 判断是否有插入数据库操作，如果有则不进行插入数据库操作
        if(!this.insertWorking) {
            this.insertWorking = true;
            // 递归插入数据
            this.insert(topic, this.kafkaOrder[0], this.kafkaOrder[0].length - 1);
        }
    }
    /**
     *
     * 插入数据库前的判断
     * @param {*} topic 获取订单的主题
     * @param {*} order 订单菜品数组
     * @param {*} index 订单下标
     */
    insert(topic, order, index) {
        if (index < 0) {
            // 用完后删除第一个数组
            this.kafkaOrder.shift();
            if (this.kafkaOrder.length == 0) {
                this.insertWorking = false;
                return;
            } else {
                this.insert(topic,this.kafkaOrder[0], this.kafkaOrder[0].length - 1);
            }
            return;
        }
        this.insertDb(topic, order, index);
    }
    /**
     *
     * 递归把订单插入数据库并编号
     * @param {*} topic 获取订单的主题
     * @param {*} order 订单菜品数组
     * @param {*} index 订单下标
     * @param {*} isSingle 判断是否只需单条插入不需要递归
     */
    insertDb(topic, order, index, isSingle = false) {
        daoClient.findOrder({}, res => {
            let nowDate = date.format(new Date(), 'YYYYMMDD');
            let updateTime = Math.round(new Date().getTime() / 1000);
            let obj = {};
            let mealId;
            let mealDate;
            if (res) {
                mealId = res._mealId || '1';
                mealDate = res.date || nowDate;
            } else {
                mealId = 0;
                mealDate = nowDate;
            }
            if (nowDate !== mealDate) {
                mealId = 1;
            } else {
                mealId++;
            }
            obj.topic = topic;
            obj.date = nowDate;
            obj._mealId = mealId;
            obj.uTime = updateTime;
            obj.foodType = order[index].foodType;
            // 设置订单状态 0：未制作 1：已下发制作 2： 制作完成 3： 已取餐 4：下发制作出错 5: 制作出错 6： 取餐超时7: 订单退款
            obj.status = 0;
            obj.orderDetail = JSON.stringify(order[index]);
            // 插入数据库
            daoClient.insertOrder([obj], (res) => {
                // console.log("数据库添加订单成功",res);
                index--
                if(!isSingle) {
                    this.insert(topic, order, index);
                }
            });
        });
    }
}
module.exports = machine;